package com.nlx.notes.module.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.nlx.notes.configuer.redis.annotation.RedisLock;
import com.nlx.notes.module.bean.dto.category.CategoryDeleteDTO;
import com.nlx.notes.module.bean.dto.category.CategorySaveDTO;
import com.nlx.notes.module.bean.dto.category.CategoryUpdateDTO;
import com.nlx.notes.module.bean.vo.CategoryVO;
import com.nlx.notes.module.bean.vo.category.CategoriesVO;
import com.nlx.notes.module.core.consts.GlobalConst;
import com.nlx.notes.module.core.enums.error.CategoryCodeEnum;
import com.nlx.notes.module.core.enums.error.UserCodeEnum;
import com.nlx.notes.module.core.util.DataConvertorUtils;
import com.nlx.notes.module.entity.Categories;
import com.nlx.notes.module.entity.IconMaterial;
import com.nlx.notes.module.entity.UserCategoryModification;
import com.nlx.notes.module.mapper.CategoriesMapper;
import com.nlx.notes.module.service.ICategoriesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nlx.notes.module.service.IIconMaterialService;
import com.nlx.notes.module.service.IUserCategoryModificationService;
import com.nlx.notes.support.handler.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author nlx
 * @since 2024-12-15
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CategoriesServiceImpl extends ServiceImpl<CategoriesMapper, Categories> implements ICategoriesService {

    final IIconMaterialService iconMaterialService;

    final IUserCategoryModificationService categoryModificationService;


    @Override
    @RedisLock(key = " 'user_category_list:' + #userId")
    public List<CategoryVO> categoryList(String categoryType,Long userId) {
        if(null == userId){
            return Collections.emptyList();
        }
        List<Categories> categoriesList = this.findListByUser(categoryType,userId);
        if(CollUtil.isEmpty(categoriesList)){
            this.saveUserCategory(userId);
            categoriesList = this.findListByUser(categoryType,userId);
        }
        //通过层级排列
        List<Categories> rootCategoryVOList = categoriesList.stream().filter(x -> null == x.getParentId()).toList();
        Map<Long, List<Categories>> subCategoryMap = categoriesList.stream().filter(x -> null != x.getParentId()).collect(Collectors.groupingBy(Categories::getParentId));
        if(CollUtil.isEmpty(rootCategoryVOList)){
            return Collections.emptyList();
        }
        List<Long> iconIds = categoriesList.stream().map(Categories::getCategoryIconId).toList();
        List<IconMaterial> iconMaterialList = iconMaterialService.findByIconIds(iconIds);
        Map<Long,String> iconUrlMap = new HashMap<>();
        if(CollUtil.isNotEmpty(iconMaterialList)){
            iconUrlMap = iconMaterialList.stream().collect(Collectors.toMap(IconMaterial::getId,IconMaterial::getIconUrl));
        }
        List<CategoryVO> categoryVOList = new ArrayList<>();
        Map<Long, String> finalIconUrlMap = iconUrlMap;
        rootCategoryVOList.forEach(rootCategory ->{
            CategoryVO categoryVO = DataConvertorUtils.copyProperties(rootCategory, CategoryVO::new);
            categoryVO.setCategoryIcon(finalIconUrlMap.get(rootCategory.getCategoryIconId()));
            List<Categories> categories = subCategoryMap.get(rootCategory.getId());
            if(CollUtil.isNotEmpty(categories)){
                List<CategoryVO> subCategoryVOList = DataConvertorUtils.copyListProperties(categories, CategoryVO::new,(source, target) -> {
                    target.setCategoryIcon(finalIconUrlMap.get(source.getCategoryIconId()));
                });
                categoryVO.setCategoryType(rootCategory.getCategoryType());
                categoryVO.setSubCategories(subCategoryVOList);
            }
            categoryVOList.add(categoryVO);
        });
        return categoryVOList;
    }

    @Override
    public List<Categories> findListByUser(String categoryType,Long userId) {
        if(null == userId){
            return Collections.emptyList();
        }
        return this.getBaseMapper().findListByUser(categoryType,userId);
//        return this.getBaseMapper().selectList(Wrappers.lambdaQuery(Categories.class)
//                .eq(Categories::getUserId, userId)
//                .eq(StrUtil.isNotEmpty(categoryType),Categories::getCategoryType,categoryType)
//                .eq(Categories::getDelFlag,GlobalConst.DEL_FLAG_NORMAL));
    }

    @Override
    public Map<Long ,String> findCategoryMapByCategoryIds(Set<Long> categoryIds) {
        List<CategoriesVO> categoriesList = this.findCategoryByCategoryIds(categoryIds);
        if(CollUtil.isEmpty(categoriesList)){
            return Collections.emptyMap();
        }
        return categoriesList.stream()
                .collect(Collectors.toMap(CategoriesVO::getId, CategoriesVO::getCategoryIcon));

    }

    @Override
    public List<CategoriesVO> findCategoryByCategoryIds(Set<Long> categoryIds) {
        if(CollUtil.isEmpty(categoryIds)){
            return null;
        }
        return this.getBaseMapper().findCategoryByCategoryIds(categoryIds);
    }

    @Override
    public String findCategoryNameMapByCategoryId(Long categoryId) {
        if(null == categoryId){
            return null;
        }
        Categories categories = this.getBaseMapper().selectById(categoryId);
        if( null == categories){
            return null;
        }
        return categories.getName();

    }

    @Override
    public Long findParentIdByCategoryId(Long categoryId) {
        return this.getBaseMapper().findParentIdByCategoryId(categoryId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveUserCategory(Long userId) {
        log.info("保存用户的分类信息");
        //判断当前用户是否存在了分类数据
        long count = this.countByUser(userId);
        if(count > 0){
            log.info("当前用户已经设置过了分类数据");
            return true;
        }
        List<Categories> categories = this.findListByDefaultCategory();
        if(CollUtil.isEmpty(categories)){
            log.warn("当前没有默认的分类数据");
            return true;
        }
        List<Categories> defaultParentCategories = new ArrayList<>();

        List<Categories> newParentCategories = new ArrayList<>();
        List<Categories> subCategories = new ArrayList<>();
        for (Categories category : categories) {
            if(category.getParentId() == null ){
                defaultParentCategories.add(category);
                newParentCategories.add(copyCategory(category,userId,null));
            }else{
                subCategories.add(copyCategory(category,userId,category.getParentId()));
            }
        }
        Map<Long,Categories> defaultParentCategoryMap = null;
        //将默认的分类转成map，key为父id
        if(CollUtil.isNotEmpty(defaultParentCategories)){
            defaultParentCategoryMap = defaultParentCategories.stream().collect(Collectors.toMap(Categories::getId, x -> x));
        }
        Map<String,Categories> newParentCategoryMap = null;
        //将默认的分类转成map，key为父id
        if(CollUtil.isNotEmpty(newParentCategories)){
            newParentCategoryMap = newParentCategories.stream().collect(Collectors.toMap(Categories::getCategoryCode, x -> x));
        }
        if(CollUtil.isNotEmpty(newParentCategories)){
            this.saveBatch(newParentCategories);
        }
        for(Categories category : subCategories){
             String parentCategoryCode = defaultParentCategoryMap.get(category.getParentId()).getCategoryCode();
            long newParentId = newParentCategoryMap.get(parentCategoryCode).getId();
            category.setParentId(newParentId);
        }
       return this.saveBatch(subCategories);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<CategoryVO> deleteCategory(Long userId, CategoryDeleteDTO categoryDeleteDTO) {
        if(null == categoryDeleteDTO.getId()){
            log.warn("删除的id不存在");
            throw new BusinessException(CategoryCodeEnum.CATEGORY_NOT_SELECT);
        }
        if(null == userId){
            throw new BusinessException(UserCodeEnum.USER_NOT_EXIST);
        }
        Categories categories= this.findByCategoryId(categoryDeleteDTO.getId(),userId);
        if(null == categories){
            log.error("需要删除的分类信息已经不存在了");
            categories = this.findByCategoryId(categoryDeleteDTO.getId(),0L);
            if(null == categories){
                log.error("当前分类名称已存在");
                throw new BusinessException(CategoryCodeEnum.CATEGORY_NOT_EXIST);
            }
            CategoryUpdateDTO categoryUpdateDTO = new CategoryUpdateDTO();
            categoryUpdateDTO.setId(categories.getId());
            categoryUpdateDTO.setCategoryName(categories.getName());
            categoryUpdateDTO.setCategoryIconId(categories.getCategoryIconId());
            categoryUpdateDTO.setCategoryType(categories.getCategoryType());
            categoryUpdateDTO.setOperationType("delete");
            categoryUpdateDTO.setUserId(userId);
            categoryModificationService.updateCategoryModification(categoryUpdateDTO);
        }else{
            //删除当前分类
            this.removeCategory(categoryDeleteDTO.getId());
            if(null == categories.getParentId()){
                return Collections.emptyList();
            }
        }

        return Collections.emptyList();
    }

    private Categories findByCategoryId(Long id, Long userId) {
        if(null == userId || id == null){
            return null;
        }
        return this.getBaseMapper().selectOne(Wrappers.lambdaQuery(Categories.class)
                .eq(Categories::getId,id)
                .eq(Categories::getUserId,userId)
                .eq(Categories::getDelFlag,GlobalConst.DEL_FLAG_NORMAL));
    }

    private void removeCategory(Long id) {
        boolean flag = this.update(Wrappers.lambdaUpdate(Categories.class)
                .set(Categories::getDelFlag,GlobalConst.DEL_FLAG_REMOVE)
                .eq(Categories::getId,id));
        if(!flag){
            log.error("删除分类发生异常，位置错误");
            throw new BusinessException(CategoryCodeEnum.DELETE_FAIL_UNKNOWN_ERROR);
        }
        this.update(Wrappers.lambdaUpdate(Categories.class)
                .set(Categories::getDelFlag,GlobalConst.DEL_FLAG_REMOVE)
                .eq(Categories::getParentId,id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<CategoryVO> updateCategory(Long userId, CategoryUpdateDTO categoryUpdateDTO) {
        if(null == userId){
            throw new BusinessException(UserCodeEnum.USER_NOT_EXIST);
        }
        Categories categories = this.findByCategoryId(categoryUpdateDTO.getId(),userId);
        if(null == categories){
            categories = this.findByCategoryId(categoryUpdateDTO.getId(),0L);
            if(null == categories){
                log.error("当前分类名称已存在");
                throw new BusinessException(CategoryCodeEnum.CATEGORY_NAME_IS_EXISTS_ERROR);
            }
            categoryUpdateDTO.setUserId(userId);
            categoryModificationService.updateCategoryModification(categoryUpdateDTO);
        }else{
            categories.setName(categoryUpdateDTO.getCategoryName());
            categories.setCategoryIcon(categoryUpdateDTO.getCateGoryIcon());
            categories.setCategoryIconId(categoryUpdateDTO.getCategoryIconId());
            categories.setUpdatedTime(LocalDateTime.now());
            this.updateById(categories);
        }

        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CategoryVO addCategory(Long userId, CategorySaveDTO categorySaveDTO) {
        if(null == userId){
            throw new BusinessException(UserCodeEnum.USER_NOT_EXIST);
        }
        //通过分类名称查询是否被使用过
        Categories categories = this.findByCategoryName(categorySaveDTO.getCategoryName(),categorySaveDTO.getCategoryType(),userId);
        if(null != categories){
            log.error("当前分类名称已存在");
            throw new BusinessException(CategoryCodeEnum.CATEGORY_NAME_IS_EXISTS_ERROR);
        }
        Categories newCat = new Categories();
        newCat.setName(categorySaveDTO.getCategoryName());
        newCat.setCategoryIconId(categorySaveDTO.getCategoryIconId());
        newCat.setUserId(userId);
        newCat.setCategoryType(categorySaveDTO.getCategoryType());
        newCat.setCreatedTime(LocalDateTime.now());
        newCat.setUpdatedTime(LocalDateTime.now());
        newCat.setDelFlag(GlobalConst.DEL_FLAG_NORMAL);
        newCat.setCategoryCode(PinyinUtil.getPinyin(categorySaveDTO.getCategoryName(),"").toLowerCase());
        if(null != categorySaveDTO.getParentId()){
            Categories parentCategory = this.findByCategoryId(categorySaveDTO.getParentId(),userId);
            if(null == parentCategory){
                parentCategory = this.findByCategoryId(categorySaveDTO.getParentId(),0L);
                if(null == parentCategory){
                    log.error("当前父分类不存在");
                    throw new BusinessException(CategoryCodeEnum.PARENT_CATEGORY_IS_NOT_EXISTS);
                }
            }
            newCat.setParentId(parentCategory.getId());
            newCat.setCategoryCode(parentCategory.getCategoryCode()+"_"+newCat.getCategoryCode());
        }
        this.save(newCat);
        return DataConvertorUtils.copyProperties(newCat,CategoryVO::new);
    }

    @Override
    public Long createCategory(String categoryName,String cateGoryType,Long userId) {
        Categories newCat = new Categories();
        newCat.setName(categoryName);
        newCat.setUserId(userId);
        newCat.setCategoryType(cateGoryType);
        newCat.setCreatedTime(LocalDateTime.now());
        newCat.setUpdatedTime(LocalDateTime.now());
        newCat.setDelFlag(GlobalConst.DEL_FLAG_NORMAL);
        newCat.setCategoryCode(PinyinUtil.getPinyin(categoryName,"").toLowerCase());
        Categories categories = this.findByCategoryName("其他", cateGoryType, userId);
        if(null == categories){
            Categories parent = new Categories();
            parent.setName("其他");
            parent.setUserId(userId);
            parent.setCategoryType(cateGoryType);
            parent.setCreatedTime(LocalDateTime.now());
            parent.setUpdatedTime(LocalDateTime.now());
            parent.setDelFlag(GlobalConst.DEL_FLAG_NORMAL);
            parent.setCategoryCode(PinyinUtil.getPinyin(categoryName,"").toLowerCase());
            this.save(parent);
            newCat.setParentId(parent.getId());
        }else{
            newCat.setParentId(categories.getId());
        }
        this.save(newCat);
        return newCat.getId();
    }

    @Override
    public Categories findByCateGoryCode(String categoryCode, Long userId) {
        if(StrUtil.isEmpty(categoryCode) || null == userId){
            return null;
        }
        List<Long> userIds = new ArrayList<>();
        userIds.add(userId);
        userIds.add(0L);
        return this.getBaseMapper().selectOne(Wrappers.lambdaQuery(Categories.class)
                .in(Categories::getUserId,userIds)
                .eq(Categories::getCategoryCode,categoryCode)
                .eq(Categories::getDelFlag,GlobalConst.DEL_FLAG_NORMAL)
                .orderByDesc(Categories::getId),false);
    }

    private Categories findByCategoryName(String categoryName,String cateGoryType, Long userId) {
        if(null == categoryName || null == userId){
            return null;
        }
        return this.getBaseMapper().selectOne(Wrappers.lambdaQuery(Categories.class)
                .eq(Categories::getName,categoryName)
                .eq(Categories::getCategoryType,cateGoryType)
                .eq(Categories::getUserId,userId)
                .eq(Categories::getDelFlag,GlobalConst.DEL_FLAG_NORMAL),true);
    }


    public static void main(String[] args) {
        System.out.println(PinyinUtil.getPinyin("你好","").toLowerCase());
    }

    private List<Categories> findListByDefaultCategory() {
        return this.getBaseMapper().selectList(Wrappers.lambdaQuery(Categories.class)
                .eq(Categories::getUserId,0)
                .eq(Categories::getDelFlag,GlobalConst.DEL_FLAG_NORMAL));
    }

    private long countByUser(Long userId) {
        if(null == userId){
            return 0;
        }
        return this.getBaseMapper().selectCount(Wrappers.lambdaQuery(Categories.class)
                .eq(Categories::getUserId,userId)
                .eq(Categories::getDelFlag, GlobalConst.DEL_FLAG_NORMAL));
    }


    private Categories copyCategory(Categories oldCat, Long userId, Long parentId) {
        Categories newCat = new Categories();
        newCat.setName(oldCat.getName());
        newCat.setCategoryCode(oldCat.getCategoryCode());
        newCat.setCategoryCode(oldCat.getCategoryCode());
        newCat.setCategoryIcon(oldCat.getCategoryIcon());
        newCat.setCategoryColor(oldCat.getCategoryColor());
        newCat.setCategoryType(oldCat.getCategoryType());
        newCat.setCategoryIconId(oldCat.getCategoryIconId());
        newCat.setParentId(parentId);
        newCat.setUserId(userId);
        newCat.setCreatedTime(LocalDateTime.now());
        newCat.setUpdatedTime(LocalDateTime.now());
        newCat.setDelFlag(GlobalConst.DEL_FLAG_NORMAL);
        return newCat;
    }

}
